package com.gmrz.uaf.crypto.KeyAttestation;

import org.bouncycastle.asn1.*;

import java.math.BigInteger;

/**
 * Created by zhangchao on 4/9/18.
 */

public class ASN1Util {

    public static byte[] getStringFromAsn1(ASN1Encodable asn1Value) throws Exception {
        if (asn1Value instanceof ASN1OctetString) {
            return ((ASN1OctetString) asn1Value).getOctets();
        } else {
            throw new Exception("OctetString value expected; found " + asn1Value.getClass().getName() + " instead.");
        }
    }

    public static int getIntegerFromAsn1(ASN1Encodable asn1Value) throws Exception {
        if(asn1Value == null){
            return -1;
        }
        if (asn1Value instanceof ASN1Integer) {
            return bigIntegerToInt(((ASN1Integer) asn1Value).getValue());
        } else if (asn1Value instanceof ASN1Enumerated) {
            return bigIntegerToInt(((ASN1Enumerated) asn1Value).getValue());
        } else {
            throw new Exception("Integer value expected; found " + asn1Value.getClass().getName() + " instead.");
        }
    }

    static int bigIntegerToInt(BigInteger bigInt) throws Exception {
        if (bigInt.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) > 0 || bigInt.compareTo(BigInteger.ZERO) < 0) {
            throw new Exception("INTEGER out of bounds");
        }
        return bigInt.intValue();
    }

    public static int[] getIntegerSetFromAsn1(ASN1Set asn1Value) throws Exception{
        if(asn1Value == null){
            return null;
        }
        ASN1Encodable[] array = asn1Value.toArray();
        int size = (asn1Value).size();
        int[] data = new int[size];
        for(int i=0;i<size;i++){
            data[i] = getIntegerFromAsn1(array[i]);
        }
        return data;
    }

    public static byte[] getBytesFromAsn1(ASN1Encodable asn1Value) throws Exception{
        if(asn1Value == null){
            return null;
        }
        if (asn1Value instanceof ASN1OctetString) {
            return ((ASN1OctetString) asn1Value).getOctets();
        } else{
            throw new Exception("OctetString value expected; found " + asn1Value.getClass().getName() + " instead.");
        }
    }

    public static boolean getBooleanFromAsn1(ASN1Encodable asn1Value) throws Exception{
        if(asn1Value == null){
            return false;
        }
        if (asn1Value instanceof ASN1Boolean) {
            return ((ASN1Boolean) asn1Value).isTrue();
        } else{
            throw new Exception("ASN1Boolean value expected; found " + asn1Value.getClass().getName() + " instead.");
        }
    }

    public static String byte2hex(byte[] raw) {
        String arHex = "0123456789ABCDEF";
        StringBuilder hex = new StringBuilder(2 * raw.length);
        for (byte b : raw) {
            hex.append("0123456789ABCDEF".charAt((b & 0xF0) >> 4)).append("0123456789ABCDEF".charAt(b & 0xF));
        }
        return hex.toString();
    }

    public static byte[] hex2byte(String hexs) {
        byte[] res = new byte[hexs.length()/2];
        char[] chs = hexs.toCharArray();
        for(int i=0,c=0; i<chs.length; i+=2,c++){
            res[c] = (byte) (Integer.parseInt(new String(chs,i,2), 16));
        }
        return res;
    }

    //focus on little endian
    public static final short getShort (byte[] bArray, int bOff) {
        return (short) (((bArray[bOff + 1] << 8) | bArray[bOff + 0] & 0xff));
    }

    public static final short setShort16(byte[] bArray, short bOff, short sValue){
        bArray[bOff + 1] = (byte) (sValue >> 8);
        bArray[bOff + 0] = (byte) (sValue >> 0);
        return (short)(bOff + 2);
    }

    public static final short setShort8(byte[] bArray, short bOff, short sValue){
        bArray[bOff + 0] = (byte) (sValue >> 0);
        return (short)(bOff + 1);
    }


    public static int byteArrayToInt(byte[] b) {
        int length = b.length;
        int value = 0;
        for(int i=0;i<length;i++){
            value = value | ((b[i] & 0xff) << (length-1-i)*8);
        }
        return value;
    }

    public static byte[] intToByteArray(int a) {
        return new byte[] {
                (byte) ((a >> 24) & 0xFF),
                (byte) ((a >> 16) & 0xFF),
                (byte) ((a >> 8) & 0xFF),
                (byte) (a & 0xFF)
        };
    }
}
